package io.kwdb.examples;

import io.kwdb.sdk.*;
import java.util.*;
import java.util.concurrent.*;

/** M2 Phase Validation Test Suite Tests all required ILP functionality for M2 milestone */
public class M2ValidationTest {

  private static final String HOST = "127.0.0.1";
  private static final int HTTP_PORT = 9091;
  private static final int GRPC_PORT = 9090;

  public static void main(String[] args) {
    System.out.println("=====================================");
    System.out.println(" KWDB Java SDK M2 Validation Test");
    System.out.println("=====================================\n");

    try {
      // Test 1: Single point ILP write
      System.out.println("1. SINGLE POINT ILP WRITE TEST");
      testSinglePointWrite();

      // Test 2: Batch ILP write (N=5 and N=100)
      System.out.println("\n2. BATCH ILP WRITE TEST");
      testBatchWrite(5);
      testBatchWrite(100);

      // Test 3: Special character escaping
      System.out.println("\n3. SPECIAL CHARACTER ESCAPING TEST");
      testSpecialCharacters();

      // Test 4: Integer suffix configuration
      System.out.println("\n4. INTEGER SUFFIX CONFIGURATION TEST");
      testIntegerSuffix();

      // Test 5: Error handling - invalid ILP
      System.out.println("\n5. ERROR HANDLING - INVALID ILP TEST");
      testInvalidILP();

      // Test 6: Timeout test
      System.out.println("\n6. TIMEOUT TEST");
      testTimeout();

      // Test 7: Client lifecycle test
      System.out.println("\n7. CLIENT LIFECYCLE TEST");
      testClientLifecycle();

      // Test 8: Verify data with SQL query
      System.out.println("\n8. SQL VERIFICATION TEST");
      verifySQLData();

      // Test 9: Async write test
      System.out.println("\n9. ASYNC WRITE TEST");
      testAsyncWrite();

      System.out.println("\n=====================================");
      System.out.println(" ALL M2 VALIDATION TESTS PASSED ✓");
      System.out.println("=====================================");

    } catch (Exception e) {
      System.err.println("Validation test failed: " + e.getMessage());
      e.printStackTrace();
      System.exit(1);
    }
  }

  private static void testSinglePointWrite() throws Exception {
    Config config =
        Config.builder().host(HOST).httpPort(HTTP_PORT).transport(Config.Transport.ILP).build();

    try (KwdbClient client = KwdbClient.create(config)) {
      long nanoTimestamp = System.currentTimeMillis() * 1_000_000L;
      Point point =
          Point.builder("sdk_bench")
              .tag("host", "m2-single-test")
              .tag("region", "us-west")
              .field("usage", 75.5)
              .field("temp", 22.3)
              .field("free", 1024.0)
              .timestamp(nanoTimestamp)
              .build();

      Status status = client.write(point);
      if (!status.isOk()) {
        throw new RuntimeException("Single point write failed: " + status.getMessage());
      }
      System.out.println(
          "✓ Single point write: SUCCESS (nanosecond timestamp: " + nanoTimestamp + ")");
    }
  }

  private static void testBatchWrite(int batchSize) throws Exception {
    Config config =
        Config.builder().host(HOST).httpPort(HTTP_PORT).transport(Config.Transport.ILP).build();

    try (KwdbClient client = KwdbClient.create(config)) {
      List<Point> points = new ArrayList<>();
      long baseTime = System.currentTimeMillis() * 1_000_000L;

      for (int i = 0; i < batchSize; i++) {
        points.add(
            Point.builder("sdk_bench")
                .tag("host", "m2-batch-" + batchSize + "-" + i)
                .tag("region", i % 2 == 0 ? "us-east" : "us-west")
                .field("usage", 50.0 + i * 0.1)
                .timestamp(baseTime + i * 1000L)
                .build());
      }

      Status status = client.batchWrite(points);
      if (!status.isOk()) {
        throw new RuntimeException(
            "Batch write (N=" + batchSize + ") failed: " + status.getMessage());
      }
      System.out.println("✓ Batch write N=" + batchSize + ": SUCCESS");
    }
  }

  private static void testSpecialCharacters() throws Exception {
    Config config =
        Config.builder().host(HOST).httpPort(HTTP_PORT).transport(Config.Transport.ILP).build();

    try (KwdbClient client = KwdbClient.create(config)) {
      // Test various special characters
      Point point =
          Point.builder("sdk_bench")
              .tag("host", "server=1,type=test")
              .tag("region", "us west coast")
              .field("usage", 88.8)
              .timestampNow()
              .build();

      Status status = client.write(point);
      if (!status.isOk()) {
        throw new RuntimeException("Special character write failed: " + status.getMessage());
      }
      System.out.println("✓ Special character escaping: SUCCESS");

      // Print encoded ILP for verification
      if (client instanceof IlpClient) {
        IlpClient ilpClient = (IlpClient) client;
        String encoded = ilpClient.getEncoder().encode(point);
        System.out.println("  Encoded ILP: " + encoded);
      }
    }
  }

  private static void testIntegerSuffix() throws Exception {
    // Test with suffix enabled
    Config configWithSuffix =
        Config.builder()
            .host(HOST)
            .httpPort(HTTP_PORT)
            .transport(Config.Transport.ILP)
            .ilpIntegerWithSuffix(true)
            .build();

    try (IlpClient client = new IlpClient(configWithSuffix)) {
      Point point =
          Point.builder("sdk_bench")
              .tag("host", "suffix-test-enabled")
              .field("usage", 67.89)
              .field("free", 12345L)
              .timestampNow()
              .build();

      String encoded = client.getEncoder().encode(point);
      if (!encoded.contains("12345i")) {
        throw new RuntimeException("Integer suffix not found when enabled");
      }

      Status status = client.write(point);
      if (!status.isOk()) {
        throw new RuntimeException("Write with suffix failed: " + status.getMessage());
      }
      System.out.println("✓ Integer with suffix (12345i): SUCCESS");
    }

    // Test with suffix disabled
    Config configNoSuffix =
        Config.builder()
            .host(HOST)
            .httpPort(HTTP_PORT)
            .transport(Config.Transport.ILP)
            .ilpIntegerWithSuffix(false)
            .build();

    try (IlpClient client = new IlpClient(configNoSuffix)) {
      Point point =
          Point.builder("sdk_bench")
              .tag("host", "suffix-test-disabled")
              .field("usage", 98.76)
              .field("free", 54321L)
              .timestampNow()
              .build();

      String encoded = client.getEncoder().encode(point);
      if (encoded.contains("54321i")) {
        throw new RuntimeException("Integer suffix found when disabled");
      }

      Status status = client.write(point);
      if (!status.isOk()) {
        throw new RuntimeException("Write without suffix failed: " + status.getMessage());
      }
      System.out.println("✓ Integer without suffix (54321): SUCCESS");
    }
  }

  private static void testInvalidILP() throws Exception {
    Config config =
        Config.builder().host(HOST).httpPort(HTTP_PORT).transport(Config.Transport.ILP).build();

    try (IlpClient client = new IlpClient(config)) {
      // Test 1: Missing measurement
      String invalidILP = ",host=test usage=1.0";
      Status status = client.writeRaw(invalidILP);
      if (status.isOk()) {
        throw new RuntimeException("Invalid ILP should have failed");
      }
      System.out.println(
          "✓ Invalid ILP (missing measurement): Got expected error - " + status.getCode());

      // Test 2: No fields
      invalidILP = "measurement,host=test";
      status = client.writeRaw(invalidILP);
      if (status.isOk()) {
        throw new RuntimeException("Invalid ILP should have failed");
      }
      System.out.println("✓ Invalid ILP (no fields): Got expected error - " + status.getCode());
    }
  }

  private static void testTimeout() throws Exception {
    // Use very short timeout to trigger timeout error
    Config config =
        Config.builder()
            .host(HOST)
            .httpPort(HTTP_PORT)
            .transport(Config.Transport.ILP)
            .timeoutMillis(1) // 1ms timeout - should trigger timeout
            .build();

    try (KwdbClient client = KwdbClient.create(config)) {
      Point point =
          Point.builder("sdk_bench")
              .tag("host", "timeout-test")
              .field("usage", 1.0)
              .timestampNow()
              .build();

      Status status = client.write(point);
      // Due to the very short timeout, this might fail with timeout or succeed if fast enough
      if (!status.isOk()) {
        System.out.println("✓ Timeout test: Got error as expected - " + status.getCode());
      } else {
        System.out.println("✓ Timeout test: Request completed within 1ms");
      }
    }
  }

  private static void testClientLifecycle() throws Exception {
    Config config =
        Config.builder().host(HOST).httpPort(HTTP_PORT).transport(Config.Transport.ILP).build();

    IlpClient client = new IlpClient(config);

    // Write should work before close
    Point point =
        Point.builder("sdk_bench")
            .tag("host", "lifecycle-test")
            .field("usage", 1.0)
            .timestampNow()
            .build();

    Status status = client.write(point);
    if (!status.isOk()) {
      throw new RuntimeException("Write before close failed: " + status.getMessage());
    }
    System.out.println("✓ Write before close: SUCCESS");

    // Close the client
    client.close();
    System.out.println("✓ Client closed");

    // Write after close should fail
    status = client.write(point);
    if (status.isOk()) {
      throw new RuntimeException("Write after close should have failed");
    }
    System.out.println("✓ Write after close: Got expected error - " + status.getMessage());
  }

  private static void verifySQLData() throws Exception {
    Config config =
        Config.builder().host(HOST).port(GRPC_PORT).transport(Config.Transport.GRPC).build();

    try (KwdbClient client = KwdbClient.create(config)) {
      // Count all M2 test data
      String sql = "SELECT COUNT(*) as total FROM sdk_bench WHERE host LIKE 'm2-%'";
      QueryResult result = client.query(sql);

      if (!result.isSuccess()) {
        throw new RuntimeException("SQL query failed: " + result.getStatus().getMessage());
      }

      if (result.getRowCount() > 0) {
        Object count = result.getRows().get(0).getValue("total");
        System.out.println("✓ SQL COUNT(*) verification: " + count + " rows");
      }

      // Select sample data with nanosecond timestamp
      sql =
          "SELECT host, region, usage, ts FROM sdk_bench WHERE host LIKE 'm2-%' ORDER BY ts DESC LIMIT 5";
      result = client.query(sql);

      if (result.isSuccess() && result.getRowCount() > 0) {
        System.out.println(
            "✓ SQL SELECT verification: Retrieved " + result.getRowCount() + " rows");
        for (int i = 0; i < Math.min(3, result.getRowCount()); i++) {
          QueryResult.Row row = result.getRows().get(i);
          System.out.println(
              "  Row "
                  + (i + 1)
                  + ": host="
                  + row.getValue("host")
                  + ", usage="
                  + row.getValue("usage"));
        }
      }
    }
  }

  private static void testAsyncWrite() throws Exception {
    Config config =
        Config.builder().host(HOST).httpPort(HTTP_PORT).transport(Config.Transport.ILP).build();

    try (KwdbClient client = KwdbClient.create(config)) {
      Point point =
          Point.builder("sdk_bench")
              .tag("host", "m2-async-test")
              .tag("region", "async-region")
              .field("usage", 99.99)
              .timestampNow()
              .build();

      CompletableFuture<Status> future = client.writeAsync(point);
      Status status = future.get(5, TimeUnit.SECONDS);

      if (!status.isOk()) {
        throw new RuntimeException("Async write failed: " + status.getMessage());
      }
      System.out.println("✓ Async write: SUCCESS");
    }
  }
}
